// Copyright 2016 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: develop dispatcher
// ------------------------------------------------------------------

#ifndef _EDDS_DDS_BUILTINQOS_PARAMETERLIST_HPP_
#define _EDDS_DDS_BUILTINQOS_PARAMETERLIST_HPP_
#ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC

#include <edds/rtps/messages/RTPS_messages.h>
#include <edds/rtps/common/CDRMessage_t.h>
#include <edds/rtps/messages/CDRMessage.h>
#include <history/CacheChange.h>

#include <functional>
#include <deps/common/ParameterTypes.hpp>

namespace evbs {
namespace edds {
namespace dds {

/**
 * ParameterList class has static methods to update or read a list of Parameter_t
 * @ingroup PARAMETER_MODULE
 */

class ParameterList {
 public:
    /**
     * Write parameterList encapsulation to the CDRMessage.
     * @param msg Pointer to the message (the pos should be correct, otherwise the behaviour is
     * undefined).
     * @return True if correct.
     */
    static bool writeEncapsulationToCDRMsg(ertps::rtps::CDRMessage_t* const msg);

    /**
     * Update the information of a cache change parsing the inline qos from a CDRMessage
     * @param[inout] change Reference to the cache change to be updated.
     * @param[in] msg Pointer to the message (the pos should be correct, otherwise the behaviour is
     * undefined).
     * @param[out] qos_size Number of bytes processed.
     * @return true if parsing was correct, false otherwise.
     */
    static bool updateCacheChangeFromInlineQos(vbs::common::CacheChange_t& change, ertps::rtps::CDRMessage_t* const msg,
                                               uint32_t& qos_size);

    /**
     * Read a parameterList from a CDRMessage
     * @param[in] msg Reference to the message (the pos should be correct, otherwise the behaviour
     * is undefined).
     * @param[in] processor Function to process each of the parameters in the list.
     * @param[in] use_encapsulation Whether encapsulation field should be read.
     * @param[out] qos_size Number of bytes processed.
     * @return true if parsing was correct, false otherwise.
     */
    template <typename Pred>
    static bool readParameterListfromCDRMsg(ertps::rtps::CDRMessage_t& msg, const Pred processor,
                                            const bool use_encapsulation, uint32_t& qos_size) {
        qos_size = 0U;

        if (use_encapsulation) {
            // Read encapsulation
            msg.pos += 1U;
            ertps::rtps::octet encapsulation = 0U;
            (void)ertps::rtps::CDRMessage::readOctet(&msg, &encapsulation);
            if (encapsulation == PL_CDR_BE) {
                msg.msg_endian = ertps::rtps::Endianness_t::BIGEND;
            } else if (encapsulation == PL_CDR_LE) {
                msg.msg_endian = ertps::rtps::Endianness_t::LITTLEEND;
            } else {
                return false;
            }
            // Skip encapsulation options
            msg.pos += 2U;
        }

        uint32_t original_pos = msg.pos;
        bool is_sentinel = false;
        while (!is_sentinel) {
            msg.pos = original_pos + qos_size;

            ParameterId_t pid {PID_SENTINEL};
            uint16_t plength = 0U;
            bool valid = true;
            valid = ertps::rtps::CDRMessage::readUInt16(&msg, reinterpret_cast<uint16_t*>(&pid)) && valid;
            valid = ertps::rtps::CDRMessage::readUInt16(&msg, &plength) && valid;

            if (pid == PID_SENTINEL) {
                // PID_SENTINEL is always considered of length 0
                plength = 0U;
                is_sentinel = true;
            }

            qos_size += (4U + plength);

            // Align to 4 byte boundary and prepare for next iteration
            qos_size = (qos_size + 3U) & ~3U;

            if ((!valid) || ((msg.pos + plength) > msg.length)) {
                return false;
            } else if (!is_sentinel) {
                if (!processor(&msg, pid, plength)) {
                    return false;
                }
            }
        }
        return true;
    }

    /**
     * Read guid from the KEY_HASH or another specific PID parameter of a CDRMessage
     * @param[in,out] msg Reference to the message (pos should be correct, otherwise the behaviour
     * is undefined).
     * @param[in] search_pid Specific PID to search
     * @param[out] guid Reference where the guid will be written.
     * @return true if a guid is returned, false otherwise.
     */
    static bool read_guid_from_cdr_msg(ertps::rtps::CDRMessage_t& msg, const uint16_t search_pid,
                                       ertps::rtps::GUID_t& guid);

    /**
     * Read change instanceHandle from the KEY_HASH or another specific PID parameter of a
     * CDRMessage
     * @param[in,out] change Pointer to the cache change.
     * @param[in] search_pid Specific PID to search
     * @return True when instanceHandle is updated.
     */
    static bool readInstanceHandleFromCDRMsg(vbs::common::CacheChange_t* const change, const uint16_t search_pid);

    static bool writeUint32ToCDRMsg(ertps::rtps::CDRMessage_t* const msg, ParameterId_t type, uint16_t length,
                                    uint32_t value);
};

}  // namespace dds
}  // namespace edds
}  // namespace evbs

#endif  // DOXYGEN_SHOULD_SKIP_THIS_PUBLIC

#endif  // _EDDS_DDS_BUILTINQOS_PARAMETERLIST_HPP_
